package com.zzyl.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.bean.copier.CopyOptions;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.aliyun.iot20180120.Client;
import com.aliyun.iot20180120.models.*;
import com.github.pagehelper.Page;
import com.github.pagehelper.PageHelper;
import com.zzyl.base.PageResponse;
import com.zzyl.base.ResponseResult;
import com.zzyl.constant.CacheConstants;
import com.zzyl.dto.DeviceDto;
import com.zzyl.dto.DevicePageQueryDto;
import com.zzyl.entity.Device;
import com.zzyl.enums.BasicEnum;
import com.zzyl.exception.BaseException;
import com.zzyl.mapper.DeviceMapper;
import com.zzyl.properties.AliIoTConfigProperties;
import com.zzyl.service.DeviceService;
import com.zzyl.vo.DeviceDataGraphVo;
import com.zzyl.vo.DeviceVo;
import com.zzyl.vo.ProductVo;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * @Description DeviceServiceImpl
 * @Author songyu
 * @Date 2024-05-23
 */
@Service
public class DeviceServiceImpl implements DeviceService {

    @Autowired
    private StringRedisTemplate redisTemplate;

    @Autowired
    private Client client;

    @Autowired
    private AliIoTConfigProperties aliIoTConfigProperties;


    /**
     * 处理从物联网平台同步产品列表
     */
    @Override
    public void syncProductList() {

        //1.调用IOT获取产品列表数据
        //1.1 构造产品列表查询请求对象
        QueryProductListRequest queryProductListRequest = new QueryProductListRequest();
        //1.2 设置分页条件查询参数
        queryProductListRequest.setPageSize(100);
        queryProductListRequest.setCurrentPage(1);
        //公共实例
        queryProductListRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        //查询公共实例下的所有产品
        QueryProductListResponse queryProductListResponse = null;
        try {
            queryProductListResponse = client.queryProductList(queryProductListRequest);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(BasicEnum.IOT_LIST_PRODUCT_ERROR);
        }

        //判断获取响应体是否成功，响应体有数据成功，没有数据不成功
        if(!queryProductListResponse.getBody().success){
            throw new BaseException(BasicEnum.IOT_LIST_PRODUCT_ERROR);
        }
        List<ProductVo> productVoList =  queryProductListResponse.getBody().getData().getList().getProductInfo()
                .stream().map(p->{
                    return BeanUtil.toBean(p,ProductVo.class);
                }).collect(Collectors.toList());

        //2.写入redis
        redisTemplate.opsForValue().set(CacheConstants.ALL_PRODUCT_KEY, JSONUtil.toJsonStr(productVoList));
    }

    /**
     * 查询所有产品列表
     *
     * @return
     */
    @Override
    public List<ProductVo> allProduct() {
        //从缓存中获取数据
        String jsonStr = redisTemplate.opsForValue().get(CacheConstants.ALL_PRODUCT_KEY);
        //如果为空，返回空集合
        if(StrUtil.isEmpty(jsonStr)){
            return Collections.emptyList();
        }
        //转换数据，并返回
        return JSONUtil.toList(jsonStr,ProductVo.class);
    }

    @Autowired
    private DeviceMapper deviceMapper;

    /**
     * 注册设备
     *
     * @param deviceDto
     */
    @Override
    public void registerDevice(DeviceDto deviceDto) {
        //1.查询名字是否重复
        //根据设备名称查询设备的数量，数量大于0，说明设备名字重复，抛出异常结束
        Long count = deviceMapper.countByDeviceName(deviceDto.getDeviceName());
        if(count>0){
            throw new BaseException(BasicEnum.DEVICE_NAME_EXIST);
        }

        //2.调用IOT的RegisterDevice接口注册设备
        RegisterDeviceRequest registerDeviceRequest = deviceDto.getRegisterDeviceRequest();
        registerDeviceRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        RegisterDeviceResponse registerDeviceResponse = null;
        try {
            registerDeviceResponse = client.registerDevice(registerDeviceRequest);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }

        //判断注册是否成功
        if(registerDeviceResponse==null || !registerDeviceResponse.getBody().success){
            throw new BaseException(BasicEnum.IOT_REGISTER_DEVICE_ERROR);
        }
        //将deviceDto转换为Device
        Device device = BeanUtil.toBean(deviceDto, Device.class);
        //获取iot生成的设备id
        device.setIotId(registerDeviceResponse.getBody().getData().getIotId());


        //3.调用IOT的QueryProduct接口查询产品名字
        QueryProductRequest queryProductRequest = new QueryProductRequest();
        queryProductRequest.setProductKey(device.getProductKey());
        queryProductRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        QueryProductResponse queryProductResponse = null;
        try {
            queryProductResponse = client.queryProduct(queryProductRequest);
        } catch (Exception e) {
            e.printStackTrace();
            //获取失败，就失败，不结束程序
        }
        if(queryProductResponse!=null && queryProductResponse.getBody().success){
            //判断查询是否成功，成功才可以设置产品名字
            device.setProductName(queryProductResponse.getBody().getData().getProductName());
        }

        //4.判断设备类型locationType是否为0，如果为设置物理位置类型为-1
        if(device.getLocationType()==0){
            device.setPhysicalLocationType(-1);
        }

        //5.调用数据库保存设备
        //设置是否包含门禁为0
        device.setHaveEntranceGuard(0);
        try {
            //调用数据库保存设备
            deviceMapper.insertSelective(device);
        } catch (Exception e) {
            //6.保存发生异常，调用IOT的DeleteDevice删除对应设备

            //删除iot中的设备数据
            DeleteDeviceRequest deleteDeviceRequest = new DeleteDeviceRequest();
            deleteDeviceRequest.setDeviceName(device.getDeviceName());
            deleteDeviceRequest.setIotId(device.getIotId());
            deleteDeviceRequest.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
            deleteDeviceRequest.setProductKey(device.getProductKey());

            try {
                client.deleteDevice(deleteDeviceRequest);
            } catch (Exception ex) {
                throw new BaseException(BasicEnum.IOT_DELETE_DEVICE_ERROR);
            }
            throw new BaseException(BasicEnum.RE_SELECT_PRODUCT);
        }


    }

    /**
     * 设备分页查询
     *
     * @param devicePageQueryDto
     * @return
     */
    @Override
    public PageResponse<DeviceVo> pageQueryDevice(DevicePageQueryDto devicePageQueryDto) {
        PageHelper.startPage(devicePageQueryDto.getPageNum(),devicePageQueryDto.getPageSize());
        Page<DeviceVo> deviceVos = deviceMapper.selectByPage(devicePageQueryDto);
        return PageResponse.of(deviceVos,DeviceVo.class);

    }

    /**
     * 查询设备详情数据
     *
     * @param request
     * @return
     */
    @Override
    public DeviceVo queryDeviceDetail(QueryDeviceDetailRequest request) {
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        //1.调用IOT的查询设备详情接口获取数据
        QueryDeviceDetailResponse response  = null;

        try {
            response = client.queryDeviceDetail(request);
        } catch (Exception e) {
            e.printStackTrace();
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
        if(response==null || !response.getBody().success){
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_ERROR);
        }
        QueryDeviceDetailResponseBody.QueryDeviceDetailResponseBodyData data = response.getBody().getData();
        //2.调用mapper根据设备id查询设备数据返回
        DeviceVo deviceVo = deviceMapper.selectByIotId(request.getIotId());

        //3.将两个数据合并，返回
        //CopyOptions.create().ignoreNullValue() 将data对象中非空属性值赋值给deviceVo
        BeanUtil.copyProperties(data,deviceVo, CopyOptions.create().ignoreNullValue());
        return deviceVo;
    }

    /**
     * 查询指定设备的物模型运行状态
     * @param request 查询设备的物模型运行状态请求模型
     * @return 物模型运行状态
     */
    @Override
    public ResponseResult queryDevicePropertyStatus(QueryDevicePropertyStatusRequest request) {
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        QueryDevicePropertyStatusResponse response;
        try {
            response = client.queryDevicePropertyStatus(request);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_QUERY_DEVICE_PROPERTY_STATUS_ERROR);
        }
        return ResponseResult.success(response.getBody().getData());
    }

    /**
     * 查看指定产品的已发布物模型中的功能定义详情
     *
     * @param request 查询已发布物模型请求模型
     * @return 已发布物模型中的功能定义详情
     */
    @Override
    public ResponseResult queryThingModelPublished(QueryThingModelPublishedRequest request) {
        request.setIotInstanceId(aliIoTConfigProperties.getIotInstanceId());
        QueryThingModelPublishedResponse response;
        try {
            response = client.queryThingModelPublished(request);
        } catch (Exception e) {
            throw new BaseException(BasicEnum.IOT_QUERY_THING_MODEL_PUBLISHED_ERROR);
        }
        return ResponseResult.success(response.getBody().getData());
    }

    /**
     * 按日查询设备数据
     *
     * @param iotId
     * @param functionId
     * @param startTime
     * @param endTime
     * @return
     */
    @Override
    public List<DeviceDataGraphVo> queryDeviceDataListByDay(String iotId, String functionId, Long startTime, Long endTime) {
        //1.先调用mapper获取根据事件范围按小时统计心率的数据List<DeviceDataGraphVo>（里面dateTime有值，里面dataValue有值，但是小时时间段不全）
        LocalDateTime minCreateTime = LocalDateTimeUtil.of(startTime);
        LocalDateTime maxCreateTime = LocalDateTimeUtil.of(endTime);
        List<DeviceDataGraphVo> deviceDataGraphVoListDB =
                deviceMapper.queryDeviceDataListByDay(iotId,functionId,minCreateTime,maxCreateTime);

        //2.获取0~23小时的数据集合List<DeviceDataGraphVo>，里面只有dateTime有值，里面dataValue没有值，小时时间段全
        List<DeviceDataGraphVo> deviceDataGraphVoList = DeviceDataGraphVo.dayInstance(minCreateTime);

        //3.遍历24小时数据集合List<DeviceDataGraphVo>，与数据库获取匹配，将数据库对应小时的数据封装给当前集合对应小时的值
        if(ObjectUtil.isNotEmpty(deviceDataGraphVoListDB)) {
            deviceDataGraphVoList.forEach(deviceDataGraphVo -> {
                deviceDataGraphVoListDB.forEach(item->{
                    if(deviceDataGraphVo.getDateTime().equals(item.getDateTime())){
                        deviceDataGraphVo.setDataValue(item.getDataValue());
                    }
                });
            });
        }
        return deviceDataGraphVoList;
    }

    /**
     * 按周查询设备数据
     *
     * @param iotId
     * @param functionId
     * @param startTime
     * @param endTime
     * @return
     */
    @Override
    public List<DeviceDataGraphVo> queryDeviceDataListByWeek(String iotId, String functionId, Long startTime, Long endTime) {
        //1.先调用mapper获取根据事件范围按小时统计心率的数据List<DeviceDataGraphVo>（里面dateTime有值，里面dataValue有值，但是小时时间段不全）
        LocalDateTime minCreateTime = LocalDateTimeUtil.of(startTime);
        LocalDateTime maxCreateTime = LocalDateTimeUtil.of(endTime);
        List<DeviceDataGraphVo> deviceDataGraphVoListDB =
                deviceMapper.queryDeviceDataListByWeek(iotId,functionId,minCreateTime,maxCreateTime);

        //2.获取一周每天数据集合List<DeviceDataGraphVo>，里面只有dateTime有值，里面dataValue没有值，小时时间段全
        List<DeviceDataGraphVo> deviceDataGraphVoList = DeviceDataGraphVo.weekInstance(minCreateTime);

        //3.遍历一周每天数据集合List<DeviceDataGraphVo>，与数据库获取匹配，将数据库对应每天的数据封装给当前集合对应每天的值
        if(ObjectUtil.isNotEmpty(deviceDataGraphVoListDB)) {
            deviceDataGraphVoList.forEach(deviceDataGraphVo -> {
                deviceDataGraphVoListDB.forEach(item->{
                    if(deviceDataGraphVo.getDateTime().equals(item.getDateTime())){
                        deviceDataGraphVo.setDataValue(item.getDataValue());
                    }
                });
            });
        }
        return deviceDataGraphVoList;
    }
}
